/*
 * JSONUtil.java
 * Copyright (c) 2014, 2019, Kiri. 
 * All rights reserved.
 */
package com.onesalt.utils;

import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * JSON
 *
 * @author kiri
 * @date 2015年3月7日
 * @version 1.0
 */
public class JSONUtil {

	/**
	 * Serialize a java bean to a json string
	 * 
	 * @param jsonObject - must be a java bean or container (Array or Hashtable)
	 * @return
	 */
	public static final String toJsonString(Object jsonObject) {
		ObjectMapper om = getDefaultObjectMapper();
		StringWriter sw = new StringWriter();
		try {
			om.writeValue(sw, jsonObject);
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		return sw.toString();
	}

	/**
	 * Deserialize a simple json string to a java bean
	 * 
	 * @param jsonString
	 * @param cls
	 * @return
	 */
	public static final <T> T fromJsonString(String jsonString, Class<T> cls) {
		if (jsonString != null && cls != null) {
			ObjectMapper om = getDefaultObjectMapper();
			try {
				return om.readValue(jsonString, cls);
			}
			catch (Exception e) {
				e.printStackTrace();
			}
		}
		return null;
	}

	/**
	 * From a json map to a java bean class
	 * 
	 * @param jsonMap
	 * @param cls
	 * @return
	 */
	public static final <T> T fromJsonMap(Map<?, ?> jsonMap, Class<T> cls) {
		String jsonString = toJsonString(jsonMap);
		return fromJsonString(jsonString, cls);
	}

	/**
	 * If a json string describes a list, we should use this method.
	 * 
	 * @param jsonString
	 * @param classForEach
	 * @return
	 */

	@SuppressWarnings("unchecked")
	public static final <T> List<T> fromJsonStringToList(String jsonString,
			Class<T> classForEach) {
		return fromJsonString(jsonString, ArrayList.class, classForEach);
	}

	/**
	 * If a json string describes a set, we should use this method.
	 * 
	 * @param jsonString
	 * @param classForEach
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static final <T> Set<T> fromJsonStringToSet(String jsonString,
			Class<T> classForEach) {
		return fromJsonString(jsonString, HashSet.class, classForEach);
	}

	/**
	 * If a json string describes a map, we should use this method.
	 * 
	 * @param jsonString
	 * @param classForKey
	 * @param classForValue
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static final <K, V> Map<K, V> fromJsonStringToMap(String jsonString,
			Class<K> classForKey, Class<V> classForValue) {
		return fromJsonString(jsonString, HashMap.class, classForKey, classForValue);
	}

	/**
	 * If a json string describes a map, we should use this method.
	 * 
	 * @param jsonString
	 * @param classForKey
	 * @param classForValue
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static final <K, V> LinkedHashMap<K, V> fromJsonStringToLinkedHashMap(
			String jsonString, Class<K> classForKey, Class<V> classForValue) {
		return fromJsonString(jsonString, LinkedHashMap.class, classForKey, classForValue);
	}

	private static final <T> T fromJsonString(String jsonString,
			Class<T> collectionClass, Class<?>... entityClass) {
		if (jsonString != null && collectionClass != null && entityClass != null) {
			ObjectMapper om = getDefaultObjectMapper();
			JavaType type = om.getTypeFactory().constructParametricType(collectionClass,
					entityClass);
			try {
				return om.readValue(jsonString, type);
			}
			catch (Exception e) {
				e.printStackTrace();
			}
		}
		return null;
	}

	private static ObjectMapper om = null;

	private static final ObjectMapper getDefaultObjectMapper() {
		if (om == null) {
			om = new ObjectMapper();
			om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
			om.setSerializationInclusion(Include.NON_NULL);
			om.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
		}
		return om;
	}

	@SuppressWarnings("unchecked")
	public static <T> T copyObject(T src) {
		return (T) copyObject(src, src.getClass());
	}

	public static <T> T copyObject(Object src, Class<T> destCls) {
		return fromJsonString(toJsonString(src), destCls);
	}

	public static <T> List<T> copyObjectList(Object src, Class<T> classForEach) {
		return fromJsonStringToList(toJsonString(src), classForEach);
	}
}
